﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Transactions;
using System.Web;
using SBACN.WebSite.Common;
using SBACN.WebSite.Models;

namespace SBACN.WebSite.DAL
{
	public class DALHelper
	{
		public static readonly DALHelper Instance = new DALHelper();
		private DALHelper()
		{ }

		/// <summary>
		/// 全局缓存Context，用于比较性能
		/// 1、由于内存缓存，会加快读的速度
		/// 2、由于全局只有一个数据库连接，可能会存在并发瓶颈
		/// </summary>
		//private static readonly SBACNEntities _CacheContext = new SBACNEntities(); 

		/// <summary>
		/// 每个线程独立缓存当前线程的Context，避免一个线程请求开启多个数据库连接
		/// </summary>
		/// <returns></returns>
		private SBACNEntities GetCurrentThreadContextInstance()
		{
			SBACNEntities objectContext = CallContext.GetData(typeof(SBACNEntities).FullName) as SBACNEntities;
			if (objectContext == null)
			{
				objectContext = new SBACNEntities();
				CallContext.SetData(typeof(SBACNEntities).FullName, objectContext);
			}
			return objectContext;
		}

		/// <summary>
		/// 每个用户的Session中保留DBContext，保持内存缓存命中率
		/// 消耗的内存较多，而且另外一个源更新了数据，无法保证同时更新各个Session中的数据
		/// 但是内存命中快，一个用户访问的数据都加载到内存中
		/// 这个方法可能不是很合理，暂不启用
		/// </summary>
		/// <returns></returns>
		private SBACNEntities GetCurrentSessionContextInsatance()
		{
			SBACNEntities objectContext = HttpContext.Current.Session["SBACNEntities"] as SBACNEntities;
			if (objectContext == null)
			{
				objectContext = new SBACNEntities();
				HttpContext.Current.Session["SBACNEntities"] = objectContext;
			}
			return objectContext;
		}

		/// <summary>
		/// 用Linq批量获取指定类型数据
		/// </summary>
		/// <typeparam name="TSource">泛型类型</typeparam>
		/// <typeparam name="TKey">排序字段</typeparam>
		/// <param name="expression">查询条件</param>
		/// <returns></returns>
		public List<TSource> GetList<TSource>(Expression<Func<TSource, bool>> expression) where TSource : class
		{
			var _entityContext = GetCurrentThreadContextInstance();
			return _entityContext.CreateObjectSet<TSource>().Where(expression).ToList();
		}

		/// <summary>
		/// 用Linq批量获取指定类型数据
		/// </summary>
		/// <typeparam name="TSource">泛型类型</typeparam>
		/// <typeparam name="TKey">排序字段</typeparam>
		/// <param name="expression">查询条件</param>
		/// <param name="order">排序语句</param>
		/// <param name="isAsc">是否顺序排列</param>
		/// <returns></returns>
		public List<TSource> GetList<TSource, TKey>(Expression<Func<TSource, bool>> expression, Expression<Func<TSource, TKey>> order, bool isAsc) where TSource : class
		{
			var _entityContext = GetCurrentThreadContextInstance();
			//var _entityContext = _CacheContext;
			if(isAsc)
			{
				return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderBy(order).ToList();
			}
			else
			{
				return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderByDescending(order).ToList();
			}
		}

		public List<TSource> GetList<TSource, TKeyF, TKeyS>(Expression<Func<TSource, bool>> expression, Expression<Func<TSource, TKeyF>> order, bool isAsc, Expression<Func<TSource, TKeyS>> thenBy, bool isAscForThenBy) where TSource : class
		{
			var _entityContext = GetCurrentThreadContextInstance();
			//var _entityContext = _CacheContext;
			if (isAsc)
			{
				if (isAscForThenBy)
				{
					return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderBy(order).ThenBy(thenBy).ToList();
				}
				else
				{
					return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderBy(order).ThenByDescending(thenBy).ToList();
				}
			}
			else
			{
				if (isAscForThenBy)
				{
					return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderByDescending(order).ThenBy(thenBy).ToList();
				}
				else
				{
					return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderByDescending(order).ThenByDescending(thenBy).ToList();
				}
			}
		}

		/// <summary>
		/// 用Linq分页批量获取
		/// </summary>
		/// <typeparam name="TSource">泛型类型</typeparam>
		/// <typeparam name="TKey">排序字段</typeparam>
		/// <param name="expression">查询条件</param>
		/// <param name="order">排序语句</param>
		/// <param name="isAsc">是否顺序排列</param>
		/// <param name="pageSize">页大小</param>
		/// <param name="pageIndex">页码，从1开始</param>
		/// <param name="recordCount">总记录数</param>
		/// <returns></returns>
		public List<TSource> GetList<TSource, TKey>(Expression<Func<TSource, bool>> expression, Expression<Func<TSource, TKey>> order, bool isAsc, int pageSize, int pageIndex, out int recordCount) where TSource : class
		{
			var _entityContext = GetCurrentThreadContextInstance();
			recordCount = _entityContext.CreateObjectSet<TSource>().Count<TSource>(expression);
			if (isAsc)
			{
				return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderBy(order).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
			}
			else
			{
				return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderByDescending(order).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
			}
		}

		public List<TSource> GetList<TSource, TKeyF, TKeyS>(Expression<Func<TSource, bool>> expression, Expression<Func<TSource, TKeyF>> order, bool isAsc, Expression<Func<TSource, TKeyS>> thenBy, bool isAscForThenBy, int pageSize, int pageIndex, out int recordCount) where TSource : class
		{
			var _entityContext = GetCurrentThreadContextInstance();
			recordCount = _entityContext.CreateObjectSet<TSource>().Count<TSource>(expression);
			if (isAsc)
			{
				if(isAscForThenBy)
				{
					return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderBy(order).ThenBy(thenBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
				}
				else
				{
					return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderBy(order).ThenByDescending(thenBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
				}
			}
			else
			{
				if(isAscForThenBy)
				{
					return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderByDescending(order).ThenBy(thenBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
				}
				else
				{
					return _entityContext.CreateObjectSet<TSource>().Where(expression).OrderByDescending(order).ThenByDescending(thenBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
				}
			}
		}


		/// <summary>
		/// 通过sql语句获取指定类型数据的列表
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="commandText">sql语句</param>
		/// <returns></returns>
		public IList<T> GetList<T>(string commandText) where T : class
		{
			try
			{
				var _entityContext = GetCurrentThreadContextInstance();
				{
					IList<T> list = _entityContext.ExecuteStoreQuery<T>(commandText).ToList();
					return list;
				}
			}
			catch (Exception ex)
			{
				Log.Instance.WriteErrorLog(commandText, ex);
				Exception newEx = new Exception(System.Environment.StackTrace, ex);
				throw newEx;
			}
		}

		/// <summary>
		/// 通过sql语句分页获取指定类型的数据列表
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="commandText">sql语句</param>
		/// <param name="pageSize">页大小</param>
		/// <param name="pageIndex">页码，从1开始</param>
		/// <param name="recordCount">总记录数</param>
		/// <returns></returns>
		public IList<T> GetList<T>(string commandText, int pageSize, int pageIndex, out int recordCount) where T : class
		{
			recordCount = 0;
			try
			{
				var _entityContext = GetCurrentThreadContextInstance();
				{
					/*
					recordCount = _entityContext.ExecuteStoreQuery<T>(commandText).Count();
					IList<T> list = _entityContext.ExecuteStoreQuery<T>(commandText).Skip(pageIndex * pageSize).Take(pageSize).ToList();
					return list;
					 */
					 var list = _entityContext.ExecuteStoreQuery<T>(commandText);
					 recordCount = list.Count();
					 return list.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
				}
			}
			catch (Exception ex)
			{
				Log.Instance.WriteErrorLog(commandText, ex);
				Exception newEx = new Exception(System.Environment.StackTrace, ex);
				throw newEx;
			}
		}

		/// <summary>
		/// 通过Linq条件获取指定对象的第一条数据
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="expression"></param>
		/// <returns></returns>
		public T GetFirstOrDefaultObject<T>(Expression<Func<T, bool>> expression) where T : class
		{
			var _entityContext = GetCurrentThreadContextInstance();
			return _entityContext.CreateObjectSet<T>().Where(expression).FirstOrDefault();
		}

		/// <summary>
		/// 通过主键获取指定对象
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		public T GetObject<T>(string key, object value) where T : class
		{
			var _entityContext = GetCurrentThreadContextInstance();
			EntityKey entityKey = new EntityKey("SBACNEntities." + typeof(T).Name, key, value);
			object returnObject = null;
			T returnInstance = null;
			if (_entityContext.TryGetObjectByKey(entityKey, out returnObject))
			{
				returnInstance = returnObject as T;
			}
			return returnInstance;
		}

		/// <summary>
		/// 添加对象到数据库，需要手动SaveChanges
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="entity"></param>
		public void Add<T>(T entity) where T : class
		{
			var _entityContext = GetCurrentThreadContextInstance();
			_entityContext.CreateObjectSet<T>().AddObject(entity);
		}

		/// <summary>
		/// 以事务模式添加对象数据到数据库，无需手动SaveChanges
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="entity"></param>
		/// <returns></returns>
		public int AddWithTransaction<T>(T entity) where T : class
		{
			int changedCount = 0;
			using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
			{
				try
				{
					//using (var _entityContext = new SBACNEntities())
					var _entityContext = GetCurrentThreadContextInstance();
					_entityContext.CreateObjectSet<T>().AddObject(entity);
					changedCount = _entityContext.SaveChanges();
					if (changedCount > 0)
						_entityContext.AcceptAllChanges();
					scope.Complete();
				}
				catch (Exception ex)
				{
					Log.Instance.WriteErrorLog(entity, ex);
					Exception newEx = new Exception(System.Environment.StackTrace, ex);
					throw newEx;
				}
			}
			return changedCount;
		}

		/// <summary>
		/// 以事务模式批量添加数据到数据库，无需手动SaveChanges
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="entityList"></param>
		/// <returns></returns>
		public int AddListWithTransaction<T>(List<T> entityList) where T : class
		{
			int changedCount = 0;
			using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
			{
				try
				{
					//using (var _entityContext = new SBACNEntities())
					var _entityContext = GetCurrentThreadContextInstance();
					{
						foreach (T entity in entityList)
							_entityContext.AddObject(typeof(T).Name, entity);
						changedCount = _entityContext.SaveChanges();
						if (changedCount > 0)
							_entityContext.AcceptAllChanges();
						scope.Complete();
					}
				}
				catch (Exception ex)
				{
					Log.Instance.WriteErrorLog(entityList, ex);
					Exception newEx = new Exception(System.Environment.StackTrace, ex);
					throw newEx;
				}
			}
			return changedCount;
		}

		/// <summary>
		/// 创建新的指定对象，如果需要添加到数据库，则要通过Add方法，显示添加
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="entity"></param>
		/// <returns></returns>
		public T Create<T>() where T : class
		{
			var _entityContext = GetCurrentThreadContextInstance();
			return _entityContext.CreateObject<T>();
		}

		/// <summary>
		/// 删除指定对象，需要手动SaveChanges
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="entity"></param>
		public void Delete<T>(T entity) where T : class
		{
			var _entityContext = GetCurrentThreadContextInstance();
			_entityContext.CreateObjectSet<T>().DeleteObject(entity);
			//_entityContext.SaveChanges();
		}

		/// <summary>
		/// 以事务模式删除对象，无需手动SaveChanges
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="id"></param>
		/// <param name="key"></param>
		/// <returns></returns>
		public int DeleteWithTransaction<T>(string key, object value) where T : class
		{
			int changedCount = 0;
			using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
			{
				try
				{
					var context = GetCurrentThreadContextInstance();
					//using (var context = new SBACNEntities())
					{
						//建立EntityKey对象
						EntityKey entityKey = new EntityKey("SBACNEntities." + typeof(T).Name, key, value);
						//通过EntityKey找到实体
						var objResult = context.GetObjectByKey(entityKey);
						//若实体存在则删除实体
						if (objResult != null)
							context.DeleteObject(objResult);
						changedCount = context.SaveChanges();
						if (changedCount > 0)
							context.AcceptAllChanges();

						scope.Complete();
					}
				}
				catch (Exception ex)
				{
					Log.Instance.WriteErrorLog(value, ex);
					Exception newEx = new Exception(System.Environment.StackTrace, ex);
					throw newEx;
				}
			}
			return changedCount;
		}

		/// <summary>
		/// 以事务模式删除符合Linq条件的所有数据，无需手动SaveChanges
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="expression"></param>
		/// <returns></returns>
		public int DeleteWithTransaction<T>(Expression<Func<T, bool>> expression) where T : class
		{
			int changedCount = 0;
			using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
			{
				try
				{
					var context = GetCurrentThreadContextInstance();
					//using (var context = new SBACNEntities())
					{
						//根据输入的委托查找数据
						var list = context.CreateObjectSet<T>().Where(expression);
						//若存在数据，删除有关数据
						if (list.Count() > 0)
							foreach (var obj in list)
								context.DeleteObject(obj);

						changedCount = context.SaveChanges();
						if (changedCount > 0)
							context.AcceptAllChanges();

						scope.Complete();
					}
				}
				catch (Exception ex)
				{
					Log.Instance.WriteErrorLog(expression, ex);
					Exception newEx = new Exception(System.Environment.StackTrace, ex);
					throw newEx;
				}
			}
			return changedCount;
		}

		/// <summary>
		/// 以事务模式删除符合Linq条件的所有数据和关联数据，无需手动SaveChanges
		/// </summary>
		/// <typeparam name="PKEntity"></typeparam>
		/// <typeparam name="FKEntity"></typeparam>
		/// <param name="expression"></param>
		/// <returns></returns>
		public int DeleteWithTransaction<PKEntity, FKEntity>(Func<PKEntity, bool> expression)
			where PKEntity : class
			where FKEntity : class
		{
			int changedCount = 0;
			using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
			{
				try
				{
					var context = GetCurrentThreadContextInstance();
					//using (var context = new SBACNEntities())
					{
						//根据输入的委托查找数据
						var list = context.CreateObjectSet<PKEntity>().Where(expression);
						//若数目大于0，删除有关数据
						if (list.Count() > 0)
						{
							foreach (var obj in list)
							{
								//在删除前加载其导航属性
								PropertyInfo propertyInfo = typeof(PKEntity).GetProperty(typeof(FKEntity).Name);
								EntityCollection<FKEntity> FKEntityList = propertyInfo.GetValue(obj, null)
									as EntityCollection<FKEntity>;
								if (FKEntityList.Count > 0)
									FKEntityList.Load();

								context.DeleteObject(obj);
							}
						}

						changedCount = context.SaveChanges();

						if (changedCount > 0)
							context.AcceptAllChanges();

						scope.Complete();
					}
				}
				catch (Exception ex)
				{
					Log.Instance.WriteErrorLog(expression, ex);
					Exception newEx = new Exception(System.Environment.StackTrace, ex);
					throw newEx;
				}
			}
			return changedCount;
		}

		/// <summary>
		/// 保存指定类型的对象
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="entity"></param>
		public void Update<T>(T entity) where T : class
		{
			var _entityContext = GetCurrentThreadContextInstance();
			_entityContext.CreateObjectSet<T>().ApplyCurrentValues(entity);
		}

		/// <summary>
		/// 以事务模式更新指定对象
		/// </summary>
		/// <typeparam name="T">泛型类型</typeparam>
		/// <param name="entity"></param>
		/// <param name="key">主键</param>
		/// <returns></returns>
		public int UpdateWithTransaction<T>(T entity, string key) where T : class
		{
			Type type = typeof(T);
			lock (type)
			{
				int changedCount = 0;
				using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
				{
					try
					{
						var context = GetCurrentThreadContextInstance();
						//using (SBACNEntities context = new SBACNEntities())
						{
							//获取实体的Id属性
							PropertyInfo property = type.GetProperty(key);
							object id = property.GetValue(entity, null);
							//根据Id获取上下文中的对应实体
							EntityKey entityKey = new EntityKey("SBACNEntities." + type.Name, key, id);
							var objResult = context.GetObjectByKey(entityKey);
							//更新实体属性
							if (objResult != null)
								context.ApplyCurrentValues<T>(type.Name, entity);

							changedCount = context.SaveChanges();
							if (changedCount > 0)
								context.AcceptAllChanges();

							scope.Complete();
						}
					}
					catch (Exception ex)
					{
						Log.Instance.WriteErrorLog(entity, ex);
						Exception newEx = new Exception(System.Environment.StackTrace, ex);
						throw newEx;
					}
				}
				return changedCount;
			}
		}

		/// <summary>
		/// 以事务模式更新关联对象的信息
		/// </summary>
		/// <typeparam name="T1"></typeparam>
		/// <typeparam name="T2"></typeparam>
		/// <param name="entity"></param>
		/// <param name="key">主体数据的主键字段</param>
		/// <returns></returns>
		public int UpdateWithTransaction<T1, T2>(T1 entity, string key)
			where T1 : class
			where T2 : class
		{
			return 0;
			#region 暂不处理
			/*
			Type typeT1 = typeof(T1);
			Type typeT2 = typeof(T2);
			lock (typeT1)
			{
				int changedCount = 0;

				using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
				{
					try
					{
						using (var context = new SBACNEntities())
						{
							PropertyInfo property = typeT1.GetProperty(key);
							object id = property.GetValue(entity, null);

							//根据软件Id建立EntityKey对象
							EntityKey entityKey = new EntityKey("SBACNEntities." + typeT1.Name, key, id);
							//根据EntityKey查找对应对象
							T1 objT1 = context.GetObjectByKey(entityKey) as T1;
							//在上下文中更新当前对象
							if (objT1 != null)
								context.ApplyCurrentValues<T1>(typeT1.Name, entity);

							//获取外键属性
							PropertyInfo propertyInfo = typeT1.GetProperty(typeT2.Name);

							//在一对多关键时更新导航属性
							var T2List = propertyInfo.GetValue(entity, null)
								   as EntityCollection<T2>;
							if (T2List != null)
							{
								foreach (var obj in T2List.ToList())
								{
									var oldEntity = context.GetObjectByKey(obj.EntityKey);
									if (oldEntity != null)
										context.ApplyCurrentValues<T2>(typeT2.Name, obj);
								}
							}

							//在多对一，一对一关系时更新导航属性
							var objT2 = propertyInfo.GetValue(entity, null) as T2;
							if (objT2 != null)
							{
								var oldEntity = context.GetObjectByKey(objT2.EntityKey);
								if (oldEntity != null)
									context.ApplyCurrentValues<T2>(typeT2.Name, objT2);
							}

							changedCount = context.SaveChanges();
							if (changedCount > 0)
								context.AcceptAllChanges();

							scope.Complete();
						}
					}
					catch (Exception ex)
					{
						Log.Instance.WriteErrorLog(entity, ex);
						Exception newEx = new Exception(System.Environment.StackTrace, ex);
						throw newEx;
					}
				}
				return changedCount;
			}
			*/
			#endregion
		}

		//public static int Update<TEntity>(this ObjectSet<TEntity> source, Expression<Func<TEntity>> setExpression, Expression<Func<TEntity, bool>> whereExpression) where TEntity : class;    

		public void SaveChanges()
		{
			GetCurrentThreadContextInstance().SaveChanges();
		}
	}
}